<?php
namespace app\common;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\facade\Env;
use Exception;

class JWTUtils
{
    private const ALGORITHM = 'HS256';
    private const DEFAULT_EXPIRE = 86400; // 默认过期时间24小时
    private const DEFAULT_REFRESH_WINDOW = 1800; // 默认刷新窗口30分钟

    /**
     * 获取JWT密钥
     * @return string
     * @throws Exception
     */
    private static function getSecret(): string
    {
        $secret = Env::get('app.jwt_secret');
        if (empty($secret)) {
            throw new \Exception('JWT_SECRET未在.env文件中配置');
        }
        return $secret;
    }

    /**
     * 生成JWT Token
     * @param int $id 用户ID
     * @param string $username 用户名
     * @param string $role 用户角色
     * @return string JWT Token
     * @throws Exception
     */
    public static function encode(int $id, string $username, string $role): string
    {
        $payload = [
            'iss' => Env::get('APP_NAME', 'zsjs'),      // 签发者
            'sub' => 'user_auth',                       // 主题
            'aud' => Env::get('APP_HOST', ''),          // 接收方
            'id' => $id,                                // 用户ID
            'username' => $username,                    // 用户名
            'role' => $role,                            // 用户角色
            'iat' => time(),                            // 签发时间
            'exp' => time() + Env::get('JWT_EXPIRE', self::DEFAULT_EXPIRE) // 过期时间
        ];

        return JWT::encode($payload, self::getSecret(), self::ALGORITHM);
    }

    /**
     * 解析JWT Token
     * @param string $token JWT Token
     * @return array 解析后的payload
     * @throws Exception
     */
    public static function decode(string $token): array
    {
        try {
            $decoded = JWT::decode($token, new Key(self::getSecret(), self::ALGORITHM));
            return (array)$decoded;
        } catch (\Firebase\JWT\ExpiredException $e) {
            throw new \Exception('Token已过期', 401);
        } catch (\Firebase\JWT\SignatureInvalidException $e) {
            throw new \Exception('Token签名无效', 401);
        } catch (Exception $e) {
            throw new \Exception('Token解析失败: ' . $e->getMessage(), 401);
        }
    }

    /**
     * 刷新Token
     * @param string $token 旧的JWT Token
     * @return string 新的JWT Token
     * @throws Exception
     */
    public static function refresh(string $token): string
    {
        $payload = self::decode($token);

        // 检查是否在可刷新时间内
        $refreshWindow = Env::get('app.jwt_refresh_window', self::DEFAULT_REFRESH_WINDOW);
        if ($payload['exp'] - time() > $refreshWindow) {
            throw new \Exception('Token未到可刷新时间');
        }

        return self::encode($payload['id'], $payload['username'], $payload['role']);
    }
}